#ifndef GAMEENGINE_GEOMETRY_QUATERNION_H
#define GAMEENGINE_GEOMETRY_QUATERNION_H

#include <GameEngine/Geometry/geometry_settings.h>

#include <GameEngine/Geometry/Location/vector3d.h>
#include <GameEngine/Geometry/CoordinateSystems/transformer3d_orientation.h>



#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

namespace Quaternions {
	//! Coordinate index for a quaternion
	enum Index {
		//! First coordinate (aka 'x')
		First = 0,
		//! Second coordinate (aka 'y')
		Second = 1,
		//! Third coordinate (aka 'z')
		Third = 2,
		//! Fourth coordinate (aka 'w')
		Fourth = 3
	};
}



//! Class used to represent quaternions
/*!
 *  A quaternion is an hypercomplex number, represented as:
 *      Q(x,y,z,w) = x.i + y.j + z.k + w
 *  ... where i, j and k are imaginary components of the quaternion
 *
 @todo Setup unit tests
 */
template <class T>
class Quaternion {

public:
	Quaternion();
	Quaternion(const T& x, const T& y, const T& z, const T& w);
	Quaternion(const Quaternion& rhs);
	template <class R> Quaternion(const Quaternion<R>& rhs);
	virtual ~Quaternion();
	Quaternion& operator=(const Quaternion& rhs);
	template <class R> Quaternion& operator=(const Quaternion<R>& rhs);

	Quaternion operator+(const Quaternion& rhs);
	template <class R> Quaternion operator+(const Quaternion<R>& rhs);
	Quaternion& operator+=(const Quaternion& rhs);
	template <class R> Quaternion& operator+=(const Quaternion<R>& rhs);

	Quaternion operator-(const Quaternion& rhs);
	template <class R> Quaternion operator-(const Quaternion<R>& rhs);
	Quaternion& operator-=(const Quaternion& rhs);
	template <class R> Quaternion& operator-=(const Quaternion<R>& rhs);

	Quaternion operator*(const Quaternion& rhs);
	template <class R> Quaternion operator*(const Quaternion<R>& rhs);
	Quaternion& operator*=(const Quaternion& rhs);
	template <class R> Quaternion& operator*=(const Quaternion<R>& rhs);

	Quaternion operator*(const T& rhs);
	Quaternion& operator*=(const T& rhs);
	Quaternion operator/(const T& rhs);
	Quaternion& operator/=(const T& rhs);

	T square_length() const;
	T length() const;
	Quaternion& normalize();
	Quaternion normalize(const Quaternion& rhs);

	Transformer3dOrientation<T> to_rotation_matrix() const;
	template <class R, class S> static Quaternion from_vector(const Vector3d<S>& rhs, const R& angle);

	T& x();
	const T& x() const;
	T& y();
	const T& y() const;
	T& z();
	const T& z() const;
	T& w();
	const T& w() const;

	T& operator[](Quaternions::Index index);
	const T& operator[](Quaternions::Index index) const;

protected:
	//! Quaternion coordinates
	T coordinates_[4];
};

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#include <GameEngine/Geometry/Core/quaternion.hpp>

#endif
